package com.spukmk3me.scene;

import com.spukmk3me.debug.Logger;
import com.spukmk3me.math.Matrix4f;
import com.spukmk3me.math.Rectangle;
import com.spukmk3me.util.DoublyLinkedList;

/**
 *  Represent a node in scene.
 *  \details
 *  Default flags: VISIBLE | ENABLE | ANIMATE | USEBLENDING.
 *  Default plane color: 0xFFFFFFFF;
 *  Default additional color: 0;
 */
public abstract class Node
{
    public abstract String getName();
    
    /**
     *  \details Return next sibling. If this node is the last node in it's
     *  sibling list, return the first node. 
     *  @return Next sibling.
     */
    public abstract Node next();
    
    /**
     *  \details Return previous sibling. If this node is the first node in
     *  it's sibling list, return the last node.
     *  @return Previous sibling. 
     */
    public abstract Node prev();
    
    /**
     *  \details Return the direct parent of this node. Unattached node or
     *  root node (in a scene) doesn't have direct parent; returns null in
     *  this case.
     *  @return Direct parent.
     */
    public abstract Node parent();
    
    /**
     *  \details Return the first node in this node's children list. If the node
     *  doesn't have children, returns null;
     *  @return First child node.
     */
    public abstract Node child();
    
    public abstract boolean isFirstSibling();
    public abstract boolean isLastSibling();
    public abstract boolean hasChildren();
    public abstract boolean hasParent();
    
    public abstract void setFlags( int flags );
    public abstract int getFlags();
    
    public abstract void setVisible( boolean visible );
    public abstract boolean getVisible();
    public abstract void setEnable( boolean enable );
    public abstract boolean getEnable();
    public abstract void setAnimating( boolean enable );
    public abstract boolean getAnimating();
    public abstract void setBlendingUsage( boolean useBlending );
    public abstract boolean getBlendingUsage();
    public abstract void setClippingUsage( boolean useClip );
    public abstract boolean getClippingUsage();
    
    public abstract void setPlaneColor( int color );
    public abstract int getPlaneColor();
    public abstract void setAdditionalColor( int color );
    public abstract int getAdditionalColor();

    public abstract void getClip( Rectangle clipData );
    public abstract void setClip( Rectangle clipData );
    public abstract void setClip( int x, int y, int w, int h );
    
    public abstract void t( float x, float y, float z );
    public abstract void s( float sx, float sy, float sz );
    public abstract void r( float vx, float vy, float vz, float angle );
    public abstract void c( Matrix4f m );
    public abstract void reset();
    
    /**
     *  Get the transformation in form of matrix
     *  @param m Object to hold the return data.
     */
    public abstract void getMatrix( Matrix4f m );
    
    public final Entity getEntity()
    {
        return m_entity;
    }
    
    public final void setEntity( Entity entity )
    {
        if ( m_entity != null )
            m_entity.decResourceCounter();

        m_entity = entity;
        
        if ( m_entity != null )
            m_entity.incResourceCounter();
    }
    
    public final void addAnimator( Animator animator )
    {
        if ( animator == null )
        {
            /* $if SPUKMK3ME_DEBUG$ */
            Logger.trace( "Warning: attempt to pass null as argument.\n" );
            /* $endif$ */
            return;
        }
        
        if ( m_animators == null )
            m_animators = new DoublyLinkedList();
        
        m_animators.push_back( animator );
        animator.setNode( this );
    }
    
    public final void removeAnimator( Animator animator )
    {
        if ( animator == null )
        {
            /* $if SPUKMK3ME_DEBUG$ */
            Logger.trace( "Warning: attempt to pass null as argument.\n" );
            /* $endif$ */
            return;
        }
        
        if ( m_animators != null )
        {
            if ( m_animators.erase( animator ) )
                animator.setNode( null );
            /* $if SPUKMK3ME_DEBUG$ */
            else
                Logger.trace( "Warning: attempt to remove an animator which this node doesn't have.\n" );
            /* $endif$ */
            
            if ( m_animators.length() == 0 )
                m_animators = null;
        }
    }
    
    public final void removeAllAnimators()
    {
        m_animators = null;
    }
    
    public final void runAnimators( int deltaTime )
    {
        if ( m_animators != null )
        {
            DoublyLinkedList.Iterator i = m_animators.first();
            DoublyLinkedList.Iterator e = m_animators.end();
            Animator animator;
            boolean remove;
            
            for ( ; !i.equals( e ); )
            {
                animator = (Animator)i.data();
                animator.animate( deltaTime );
                remove = false;
                
                if ( (animator.getFlags() & Animator.FLAG_AUTODROP) != 0 )
                {
                    if ( !animator.isAnimating() )
                        remove = true;
                }
                
                i.fwrd();
                
                if ( remove )
                    m_animators.erase( animator );
            }
        }
    }

    public static final int VISIBLE     = 0x00000001;
    public static final int ENABLE      = 0x00000002;
    public static final int USECLIPPING = 0x00000004;
    public static final int ANIMATE     = 0x00000008;
    public static final int USEBLENDING = 0x00000010;
    
    private Entity              m_entity;
    private DoublyLinkedList    m_animators;
}